package nl.vu.few.ibivu.biomodels.server.petrinet;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import nl.vu.few.ibivu.jlemon.NodeIterator;
import nl.vu.few.ibivu.jlemon.NodeMap;
import nl.vu.few.ibivu.jlemon.listgraph.ListDigraph;
import nl.vu.few.ibivu.jlemon.listgraph.Node;

public class StateSpace extends ListDigraph {
	Petrifier petrifier;
	NodeMap<Node, Map<Node,Integer>> markings;
	HashMap<Map<Node,Integer>, Node> inverse;
	
	private void generateNodes(int nodeNum) {
		for (int i = 0; i < nodeNum; i++) {
			addNode();
		}
	}
	
	private void generateMarkings(List<Node> geneList, CombinationsIterator iterator) {
		NodeIterator<Node> nodes = new NodeIterator<Node>(this);
		while (nodes.hasNext()) {
			Node node = nodes.next();
			List<Integer> state = iterator.next();
			Map<Node, Integer> marking = petrifier.getEmptyMarking();
			ListIterator<Node> geneIterator = geneList.listIterator();
			while (geneIterator.hasNext()) {
				Node gene = geneIterator.next();
				marking.put(gene, state.get(geneIterator.previousIndex()));
			}
			markings.put(node, marking);
			inverse.put(marking, node);
		}
	}
	
	private void generateArcs() {
		NodeIterator<Node> iterator = new NodeIterator<Node>(this);
		Map<Node, Integer> tempMarking = petrifier.getEmptyMarking();
		while(iterator.hasNext()) {
			// process the next node in the state space
			Node node = iterator.next();
			// get the marking corresponding to the current node in the state space
			Map<Node, Integer> marking = markings.get(node);
			// iterates over all the transitions enabled in the current marking
			Iterator<Node> enabledIterator = petrifier.enabledTransitionIterator(marking);
			while (enabledIterator.hasNext()) {
				// set the temporary marking as the current marking
				tempMarking.putAll(marking);
				// fire the current transition
				petrifier.fire(tempMarking, enabledIterator.next());
				// retrieve the node corresponding to the marking modified by the firing rule
				Node next = inverse.get(tempMarking);
				// add the arc in the state space
				addArc(node, next);
			}
		}
	}
	
	public StateSpace build(Petrifier petrifier) {
		this.petrifier = petrifier;
		ArrayList<Node> geneList = new ArrayList<Node>();
		Iterator<Node> geneIterator = petrifier.placeIterator();
		while (geneIterator.hasNext()) {
			geneList.add(geneIterator.next());
		}
		CombinationsIterator iterator = new CombinationsIterator(petrifier.getCapacity(), geneList.size());
		generateNodes(iterator.getTotal());
		markings = new NodeMap<Node, Map<Node,Integer>>(this);
		inverse = new HashMap<Map<Node,Integer>, Node>();
		generateMarkings(geneList, iterator);
		generateArcs();
		return this;
	}
	
	public Map<Node, Integer> getMarking(Node node) {
		return markings.get(node);
	}
	
	public Node getNode(Map<Node, Integer> marking) {
		return inverse.get(marking);
	}
}
